package web

import (
	"gitee.com/chicken-c/go-learn/webook/internal/domain"
	"gitee.com/chicken-c/go-learn/webook/internal/service"
	"gitee.com/chicken-c/go-learn/webook/internal/web/jwt"
	"gitee.com/chicken-c/go-learn/webook/pkg/logger"
	"github.com/gin-gonic/gin"
	"golang.org/x/sync/errgroup"
	"net/http"
	"strconv"
	"time"
)

type ArticleHandler struct {
	svc     service.ArticleService
	intrSvc service.InterActiveService
	l       logger.Logger
	biz     string
}

func NewArticleHandler(svc service.ArticleService, l logger.Logger, intrSvc service.InterActiveService) *ArticleHandler {
	return &ArticleHandler{
		svc:     svc,
		l:       l,
		intrSvc: intrSvc,
		biz:     "article",
	}
}

func (a *ArticleHandler) RegisterRoute(server *gin.Engine) {
	g := server.Group("/articles")
	g.POST("/edit", a.Edit)
	g.POST("/publish", a.Publish)
	g.POST("/withdraw", a.WithDraw)
	//查询接口
	g.POST("/list", a.List)
	g.GET("/detail", a.Detail)

	p := server.Group("/pub")
	p.GET("/:id", a.PubDetail)
	p.POST("/like", a.Like)
	p.POST("/collect", a.Collect)
}

func (a *ArticleHandler) Publish(ctx *gin.Context) {
	type article struct {
		Id      int64  `json:"id"`
		Title   string `json:"title"`
		Content string `json:"content"`
	}

	var req article
	if err := ctx.Bind(&req); err != nil {
		return
	}

	//获取用户ID
	claims, _ := ctx.MustGet("user").(jwt.UserClaims)

	//数据库操作
	id, err := a.svc.Publish(ctx, domain.Article{
		Id:      req.Id,
		Title:   req.Title,
		Content: req.Content,
		Author: domain.Author{
			Id: claims.Id,
		},
	})
	if err != nil {
		ctx.JSON(http.StatusOK, Result{
			ErrCode: 5,
			ErrMsg:  "系统错误",
		})
		return
	}

	ctx.JSON(http.StatusOK, Result{
		ErrCode: 2,
		ErrMsg:  "发表成功",
		Data:    id,
	})
}

func (a *ArticleHandler) Edit(ctx *gin.Context) {
	type article struct {
		Id      int64  `json:"id"`
		Title   string `json:"title"`
		Content string `json:"content"`
	}

	var req article
	if err := ctx.Bind(&req); err != nil {
		return
	}

	//获取用户ID
	claims, _ := ctx.MustGet("user").(jwt.UserClaims)

	//数据库操作
	id, err := a.svc.Save(ctx, domain.Article{
		Id:      req.Id,
		Title:   req.Title,
		Content: req.Content,
		Author: domain.Author{
			Id: claims.Id,
		},
	})
	if err != nil {
		ctx.JSON(http.StatusOK, Result{
			ErrCode: 5,
			ErrMsg:  "系统错误",
		})
		return
	}

	ctx.JSON(http.StatusOK, Result{
		ErrCode: 2,
		ErrMsg:  "保存成功",
		Data:    id,
	})
}

func (a *ArticleHandler) WithDraw(ctx *gin.Context) {
	type Req struct {
		Id int64 `json:"id"`
	}

	var req Req
	if err := ctx.Bind(&req); err != nil {
		return
	}

	//获取用户ID
	claims, _ := ctx.MustGet("user").(jwt.UserClaims)
	err := a.svc.WithDraw(ctx, req.Id, claims.Id)
	if err != nil {
		ctx.JSON(http.StatusOK, Result{
			ErrCode: 5,
			ErrMsg:  "系统错误",
		})
		return
	}

	ctx.JSON(http.StatusOK, Result{
		ErrCode: 2,
		ErrMsg:  "隐藏成功",
	})
}

func (a *ArticleHandler) List(ctx *gin.Context) {
	type Req struct {
		Limit  int64 `json:"limit"`
		Offset int64 `json:"offset"`
	}
	var req Req
	if err := ctx.Bind(&req); err != nil {
		return
	}

	claims := ctx.MustGet("user").(jwt.UserClaims)
	arts, err := a.svc.List(ctx, req.Offset, req.Limit, claims.Id)
	if err != nil {
		ctx.JSON(http.StatusOK, Result{
			ErrCode: 5,
			ErrMsg:  "系统错误",
		})
	}

	//构造ArticleVO切片
	aVo := make([]ArticleVO, 0, len(arts))
	for _, art := range arts {
		aVo = append(aVo, ArticleVO{
			Id:       art.Id,
			Title:    art.Title,
			Abstract: art.AbStract(),
			Status:   art.Status.Uint8(),
			Ctime:    art.Ctime.Format(time.DateTime),
			Utime:    art.Utime.Format(time.DateTime),
		})
	}
	ctx.JSON(http.StatusOK, Result{
		ErrCode: 2,
		ErrMsg:  "查询成功",
		Data:    aVo,
	})
}

func (a *ArticleHandler) Detail(ctx *gin.Context) {
	idStr := ctx.Query("id")
	id, err := strconv.ParseInt(idStr, 10, 64)
	if err != nil {
		ctx.JSON(http.StatusOK, Result{
			ErrCode: 4,
			ErrMsg:  "错误的ID",
		})
	}

	art, err := a.svc.Detail(ctx, id)
	if err != nil {
		ctx.JSON(http.StatusOK, Result{
			ErrCode: 5,
			ErrMsg:  "系统错误",
		})
	}

	//不使用 DAO层的处理
	claims := ctx.MustGet("user").(jwt.UserClaims)
	if claims.Id != art.Author.Id {
		//作者ID和文章ID不匹配，这里就要接入告警，代表由用户攻击我们
		ctx.JSON(http.StatusOK, Result{
			ErrCode: 5,
			ErrMsg:  "系统错误",
		})
		a.l.Warn("作者Id和帖子Id不匹配", logger.Field{
			Key: "authod_id",
			Val: claims.Id,
		})
		return
	}

	ctx.JSON(http.StatusOK, Result{
		ErrCode: 2,
		ErrMsg:  "查询成功",
		Data: ArticleVO{
			Id:      id,
			Title:   art.Title,
			Content: art.Content,
			Status:  art.Status.Uint8(),
			Ctime:   art.Ctime.Format(time.DateTime),
			Utime:   art.Utime.Format(time.DateTime),
		},
	})
}

func (a *ArticleHandler) PubDetail(ctx *gin.Context) {
	idStr := ctx.Param("id")
	id, err := strconv.ParseInt(idStr, 10, 64)
	if err != nil {
		ctx.JSON(http.StatusOK, Result{
			ErrCode: 4,
			ErrMsg:  "帖子id不正确",
		})
	}

	var (
		eg   errgroup.Group
		art  domain.Article
		intr domain.InterActive
	)

	claims := ctx.MustGet("user").(jwt.UserClaims)

	//获取文章详情
	eg.Go(func() error {
		art, err = a.svc.PubDetail(ctx, id, claims.Id)
		return err
	})

	//我们在这里聚合，获取阅读，点赞，收藏相关
	eg.Go(func() error {
		claims := ctx.MustGet("user").(jwt.UserClaims)
		intr, err = a.intrSvc.Get(ctx, a.biz, id, claims.Id)
		if err != nil {
			//这个错误不影响主要业务
			a.l.Warn("获取文章阅读数相关错误", logger.Field{
				Key: "err",
				Val: err,
			})
		}

		return err
	})

	//等待两个goroutine
	err = eg.Wait()
	if err != nil {
		ctx.JSON(http.StatusOK, Result{
			ErrCode: 5,
			ErrMsg:  "系统错误",
		})
	}

	//更新一下阅读数
	//go func() {
	//	er := a.intrSvc.IncrReadCnt(ctx, a.biz, art.Id)
	//	if er != nil {
	//		a.l.Warn("更新阅读数失败", logger.Field{
	//			Key: "err",
	//			Val: er,
	//		})
	//	}
	//}()

	ctx.JSON(http.StatusOK, Result{
		ErrCode: 2,
		ErrMsg:  "查询成功",
		Data: ArticleVO{
			Id:      art.Id,
			Title:   art.Title,
			Content: art.Content,
			Author: domain.Author{
				Id:       art.Author.Id,
				Nickname: art.Author.Nickname,
			},
			Status:      art.Status.Uint8(),
			Interactive: intr,
			Ctime:       art.Ctime.Format(time.DateTime),
			Utime:       art.Utime.Format(time.DateTime),
		},
	})
}

func (a *ArticleHandler) Like(ctx *gin.Context) {
	var req LikeReq
	if err := ctx.Bind(&req); err != nil {
		return
	}

	claims := ctx.MustGet("user").(jwt.UserClaims)
	err := a.intrSvc.Like(ctx, req.Like, a.biz, req.Id, claims.Id)
	if err != nil {
		ctx.JSON(http.StatusOK, Result{
			ErrCode: 5,
			ErrMsg:  "系统错误",
		})
		return
	}

	ctx.JSON(http.StatusOK, Result{
		ErrCode: 2,
		Data:    "OK",
	})
}

func (a *ArticleHandler) Collect(ctx *gin.Context) {
	var req CollectReq
	if err := ctx.Bind(&req); err != nil {
		return
	}

	claims := ctx.MustGet("user").(jwt.UserClaims)
	err := a.intrSvc.Collect(ctx, req.Collect, a.biz, req.Id, req.cId, claims.Id)
	if err != nil {
		ctx.JSON(http.StatusOK, Result{
			ErrCode: 5,
			ErrMsg:  "系统错误",
		})
		return
	}

	ctx.JSON(http.StatusOK, Result{
		ErrCode: 2,
		Data:    "OK",
	})
}
